/**
 * Parameter validation utilities
 */

import type {
  ParameterSchema,
  ParameterValues,
  ValidationResult,
  ValidationError
} from '../types';

/**
 * Validates parameters against their schemas
 * @param parameters - Parameter values to validate
 * @param schemas - Parameter schemas to validate against
 * @returns Validation result
 */
export function validateParameters(
  parameters: ParameterValues,
  schemas: Record<string, ParameterSchema>
): ValidationResult {
  const errors: ValidationError[] = [];

  // Check required parameters
  for (const [name, schema] of Object.entries(schemas)) {
    if (schema.required && !(name in parameters)) {
      errors.push({
        path: name,
        message: 'Required parameter is missing',
        value: undefined
      });
      continue;
    }

    // Skip validation if parameter is not provided and not required
    if (!(name in parameters)) {
      continue;
    }

    const value = parameters[name];
    const paramErrors = validateParameter(value, schema, name);
    errors.push(...paramErrors);
  }

  // Check for unknown parameters
  for (const name of Object.keys(parameters)) {
    if (!(name in schemas)) {
      errors.push({
        path: name,
        message: 'Unknown parameter',
        value: parameters[name]
      });
    }
  }

  return {
    valid: errors.length === 0,
    errors
  };
}

/**
 * Validates a single parameter value against its schema
 * @param value - Parameter value to validate
 * @param schema - Parameter schema
 * @param path - Parameter path for error reporting
 * @returns Array of validation errors
 */
export function validateParameter(
  value: unknown,
  schema: ParameterSchema,
  path: string = ''
): ValidationError[] {
  const errors: ValidationError[] = [];

  // Type validation
  if (!validateType(value, schema.type)) {
    errors.push({
      path,
      message: `Expected type ${schema.type}, got ${typeof value}`,
      value
    });
    return errors; // Don't continue if type is wrong
  }

  // Enum validation
  if (schema.enum && !schema.enum.includes(value)) {
    errors.push({
      path,
      message: `Value must be one of: ${schema.enum.map(v => JSON.stringify(v)).join(', ')}`,
      value
    });
  }

  // Type-specific validations
  switch (schema.type) {
    case 'string':
      errors.push(...validateString(value as string, schema, path));
      break;
    case 'number':
      errors.push(...validateNumber(value as number, schema, path));
      break;
    case 'array':
      errors.push(...validateArray(value as unknown[], schema, path));
      break;
    case 'object':
      errors.push(...validateObject(value as Record<string, unknown>, schema, path));
      break;
  }

  return errors;
}

/**
 * Validates type compatibility
 * @param value - Value to check
 * @param expectedType - Expected type
 * @returns True if type matches
 */
function validateType(value: unknown, expectedType: string): boolean {
  switch (expectedType) {
    case 'string':
      return typeof value === 'string';
    case 'number':
      return typeof value === 'number' && !isNaN(value);
    case 'boolean':
      return typeof value === 'boolean';
    case 'array':
      return Array.isArray(value);
    case 'object':
      return typeof value === 'object' && value !== null && !Array.isArray(value);
    default:
      return false;
  }
}

/**
 * Validates string parameters
 * @param value - String value to validate
 * @param schema - Parameter schema
 * @param path - Parameter path
 * @returns Array of validation errors
 */
function validateString(
  value: string,
  schema: ParameterSchema,
  path: string
): ValidationError[] {
  const errors: ValidationError[] = [];

  // Pattern validation
  if (schema.pattern) {
    const regex = new RegExp(schema.pattern);
    if (!regex.test(value)) {
      errors.push({
        path,
        message: `String does not match pattern: ${schema.pattern}`,
        value
      });
    }
  }

  // Length validation
  if (schema.min !== undefined && value.length < schema.min) {
    errors.push({
      path,
      message: `String length must be at least ${schema.min}`,
      value
    });
  }

  if (schema.max !== undefined && value.length > schema.max) {
    errors.push({
      path,
      message: `String length must be at most ${schema.max}`,
      value
    });
  }

  return errors;
}

/**
 * Validates number parameters
 * @param value - Number value to validate
 * @param schema - Parameter schema
 * @param path - Parameter path
 * @returns Array of validation errors
 */
function validateNumber(
  value: number,
  schema: ParameterSchema,
  path: string
): ValidationError[] {
  const errors: ValidationError[] = [];

  // Range validation
  if (schema.min !== undefined && value < schema.min) {
    errors.push({
      path,
      message: `Number must be at least ${schema.min}`,
      value
    });
  }

  if (schema.max !== undefined && value > schema.max) {
    errors.push({
      path,
      message: `Number must be at most ${schema.max}`,
      value
    });
  }

  return errors;
}

/**
 * Validates array parameters
 * @param value - Array value to validate
 * @param schema - Parameter schema
 * @param path - Parameter path
 * @returns Array of validation errors
 */
function validateArray(
  value: unknown[],
  schema: ParameterSchema,
  path: string
): ValidationError[] {
  const errors: ValidationError[] = [];

  // Length validation
  if (schema.min !== undefined && value.length < schema.min) {
    errors.push({
      path,
      message: `Array length must be at least ${schema.min}`,
      value
    });
  }

  if (schema.max !== undefined && value.length > schema.max) {
    errors.push({
      path,
      message: `Array length must be at most ${schema.max}`,
      value
    });
  }

  // Item validation
  if (schema.items) {
    for (let i = 0; i < value.length; i++) {
      const itemErrors = validateParameter(value[i], schema.items, `${path}[${i}]`);
      errors.push(...itemErrors);
    }
  }

  return errors;
}

/**
 * Validates object parameters
 * @param value - Object value to validate
 * @param schema - Parameter schema
 * @param path - Parameter path
 * @returns Array of validation errors
 */
function validateObject(
  value: Record<string, unknown>,
  schema: ParameterSchema,
  path: string
): ValidationError[] {
  const errors: ValidationError[] = [];

  // Property validation
  if (schema.properties) {
    for (const [propName, propSchema] of Object.entries(schema.properties)) {
      const propPath = path ? `${path}.${propName}` : propName;
      const propValue = value[propName];

      if (propSchema.required && !(propName in value)) {
        errors.push({
          path: propPath,
          message: 'Required property is missing',
          value: undefined
        });
        continue;
      }

      if (propName in value) {
        const propErrors = validateParameter(propValue, propSchema, propPath);
        errors.push(...propErrors);
      }
    }
  }

  return errors;
}
